package database

import (
	"go-redis/datastruct/dict"
	"go-redis/interface/database"
	"go-redis/interface/resp"
	"go-redis/resp/reply"
	"strings"
)

// DB 存储执行的数据
type DB struct {
	index int
	dict  dict.Dict
	// 每个db的aof处理函数
	addAof func(CmdLine)
}

// CmdLine is alias for [][]byte, represents a command line
type CmdLine = [][]byte

//MakeDB 构造创建DB实例的方法
func MakeDB() *DB {
	return &DB{
		dict: dict.MakeSyncDict(),
	}
}

//Exec redis执行对应的命令
func (db *DB) Exec(c resp.Connection, cmdLine [][]byte) resp.Reply {

	cmdTableIdx := strings.ToLower(string(cmdLine[0]))
	command, ok := cmdTable[cmdTableIdx]
	if !ok {
		return reply.MakeErrReply("ERR unknown command '" + cmdTableIdx + "'")
	}
	if !validateArity(command.arity, cmdLine) {
		return reply.MakeArgNumErrReply(cmdTableIdx)
	}
	// 拿到执行的函数
	executorFunc := command.executor
	// 执行对应的命令函数 cmdLine[1:]参数列表
	return executorFunc(db, cmdLine[1:])
}

//validateArity 验证实际的参数列表与命令行的参数列表是否一致
func validateArity(arity int, cmdArgs [][]byte) bool {
	actualLen := len(cmdArgs)
	if arity >= 0 {
		return actualLen == arity
	}
	return actualLen >= -arity
}

//ExecFunc 定义redis命令的执行函数
type ExecFunc func(db *DB, args [][]byte) resp.Reply

/* ---- data Access ----- */

// GetEntity returns DataEntity bind to given key
func (db *DB) GetEntity(key string) (*database.DataEntity, bool) {

	raw, ok := db.dict.Get(key)
	if !ok {
		return nil, false
	}
	entity, _ := raw.(*database.DataEntity)
	return entity, true
}

// PutEntity a DataEntity into DB
func (db *DB) PutEntity(key string, entity *database.DataEntity) int {
	return db.dict.Put(key, entity)
}

// PutIfExists edit an existing DataEntity
func (db *DB) PutIfExists(key string, entity *database.DataEntity) int {
	return db.dict.PutIfExists(key, entity)
}

// PutIfAbsent insert an DataEntity only if the key not exists
func (db *DB) PutIfAbsent(key string, entity *database.DataEntity) int {
	return db.dict.PutIfAbsent(key, entity)
}

// Remove the given key from db
func (db *DB) Remove(key string) {
	db.dict.Remove(key)
}

// Removes the given keys from db
func (db *DB) Removes(keys ...string) (deleted int) {
	deleted = 0
	for _, key := range keys {
		_, exists := db.dict.Get(key)
		if exists {
			db.Remove(key)
			deleted++
		}
	}
	return deleted
}

// Flush clean database
func (db *DB) Flush() {
	db.dict.Clear()

}
